//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
//---------------------------------------------------------------------------
// Net MAIN.C
//---------------------------------------------------------------------------
#include "lh7a400_map.h"
#include "LH7A400_drivers.h"
#include "watertek_drivers.h"
#include "net_net.h"
#include "watertek_main.h"

#define _DEBUG

UNS_8 my_ip[32]    = "192.168.2.54";
UNS_8 remote_ip[32]= "192.168.1.54";

#ifdef _DEBUG
UNS_8 file_name[256]= "ela20_gnu_arm.dbg\\bin\\elastos.img";
#else
UNS_8 file_name[256]= "ela20_gnu_arm.rls\\bin\\elastos.img";
#endif

UNS_32 my_ipaddr;

UNS_16 my_port;

/*tftp server ip address:*/
UNS_32 remote_ipaddr;

#define TFTP_PORT      INTSWAP(69)

#define MAX_TFTP_DATA  1432  /*** Maximum number of bytes in a TFTP
                               message (512 bytes plus header)***/

#define MAX_RETRIES    4     /* Maximum number of times a message
                               will be transmitted due to timeouts***/

#define FILE_NAME_MAX_LEN  256  /*file name max length*/
/*
 * Data direction of the TFTP link
 */
typedef enum {
    H2OLink,                 /* The link is transfering data from the host to odo*/
    O2HLink                  /* The link is transfering data from odo to the host*/
} TFtpLinkDir;

/*buffer for user-data-of-udp*/
UNS_8 Buffer[MAX_TFTP_DATA]; /*** The transmit/receive data that
                               is currently being attempted***/

/*the file downloaded will be saved here*/
UNS_8 *save_addr = (UNS_8 *)0xC0010000;
                  //NULL;

UDP_DATA udp_data;

UNS_32 BootupState = 0;//Mark NetBootup State
//1: icmp ok
//2: TFTP open
//3: Recv data over

UNS_32 bIsWriteFlash = 0;// Mark write config to flahs or not
UNS_32 nTimerCount = 0;

UNS_32  last_block_id = 0, nLength = 0;

UNS_8   sendbuf[3000];

void RecvTftpData();
UNS_16 GenerateSrcPort();
void TFtpOpen(UNS_8* inbuf, UNS_32 dest_ipaddr,
                UNS_8 *pfilename, TFtpLinkDir DataDir);
void CRT_Init();
////////////////////////////////////////////////////////////////////////////
// Global variables
UNS_16 volatile event_word;

// This sets my hardware address to 00:01:02:03:04:05
UNS_8  my_hwaddr[6] = {0x00, 0x01, 0x02, 0x03, 0x00, 0x00};

// Hardware addr to send a broadcast
UNS_8  broadcast_hwaddr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// This sets my IP address to 192.168.23.10
//UNS_32 my_ipaddr = 0x0A17A8C0;

//lcd
extern UNS_16 lcdCurX;
extern UNS_16 lcdCurY;

extern UNS_32 Dm_waitForInterrup;
extern UNS_32 Dm_waitForReset;

//--------------------------------------------------------------------------
// Initialize the memory management routines
// Initialize variables declared in main
//--------------------------------------------------------------------------
void init_main(void)
{
    Dm_waitForInterrup = TRUE;
    Dm_waitForReset = FALSE;
    event_word = 0;
}

//--------------------------------------------------------------------------
// Timer 2 interrupt service routing. Intr vector location is
// address 0x002B. The timer generates an interrupt every 25 msec
// It is set to auto reload so do not need to reload it.
//--------------------------------------------------------------------------
void NET_Loop25ms(void)
{
//    static UNS_16 count1 = 0;
//    static UNS_16 count2 = 0;

    nTimerCount++;

//    count1++;
//    if (count1 == 20)
//    {
//        // These events happens every 0.50 seconds, not simultaneously
//        count1 = 0;
//        //event_word |= EVENT_ARP_RETRANSMIT;
//    }
//
//    count2++;
//    if (count2 == 2401)
//    {
//        // This happens every 60.025 seconds, not simultaneous
//        // with above tasks
//        count2 = 0;
//        //event_word |= EVENT_AGE_ARP_CACHE;
//    }
}

void NET_Init(void)
{
    init_main();
    init_arp();
    Dm_Init();
}

   // The code below is a priority based RTOS.  The event
   // handlers are in priority order - highest priority first.
void NET_MainLoop(void)
{
    UNS_16 event_word_copy;
    UNS_8  *inbuf;

    // Query dm9000 to see if Ethernet frame has arrived
    // If so, set EVENT_ETH_ARRIVED bit in event_word
    Dm_Query();

    // Use a copy of event word to avoid interference
    // with interrupts
    event_word_copy = event_word;

    // See if an Ethernet frame has arrived
    if (event_word_copy & EVENT_ETH_ARRIVED) {
        event_word &= (~EVENT_ETH_ARRIVED);

        // Allocate a buffer and read frame from dm9000
        inbuf = Dm_RcveFrame();
        if (inbuf != NULL) {
            DBGMSG("***** Receive a packet ! *****" );
            // Process the received Ethernet frame
            ETH_Rcve(inbuf);
        }
    }

//    // See if ARP retransmit timer has expired
//    else if (event_word_copy & EVENT_ARP_RETRANSMIT)
//    {
//        event_word &= (~EVENT_ARP_RETRANSMIT);
//        arp_retransmit();
//    }
//
//    // See if it is time to age the ARP cache
//    else if (event_word_copy & EVENT_AGE_ARP_CACHE)
//    {
//        event_word &= (~EVENT_AGE_ARP_CACHE);
//        age_arp_cache();
//    }

#if 1
    if (BootupState == 0) {
        PING_HEADER *ping_packet = (PING_HEADER *)(sendbuf + ETH_HEADER_LEN + IP_HEADER_LEN);
        UNS_16 data_len = 13; /*len of const string below*/
//        ping_packet->echo_data =
        memcpy(&(ping_packet->echo_data), "hello, world!", data_len);
        ping_packet->identifier = INTSWAP(8888);
        ping_packet->sequence = INTSWAP(9999);
        icmp_send(sendbuf, remote_ipaddr, 8, 0, PING_HEADER_LEN + data_len);

        Dm_Delay( 50 );

        return;
    }
    else if (BootupState > 1) {
        return;
    }

    dputs("***** Connect to TFTP ! ***** \n" );
    /*get file by tftp*/
    TFtpOpen(sendbuf, remote_ipaddr, file_name, H2OLink);
    BootupState = 2;//Tftp Open
#endif
}
////////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------------
// This routine will open a new link to the host. The DataDir parameter
// specifies if this is going to be a request to read (H2OLink) or write
// (O2HLink) data.  The routine will return a non-zero link handle,
// which is actually the iLinkSlot+1, if there is a link slot available.
// Otherwise, it will return 0.
//--------------------------------------------------------------------------
void TFtpOpen(UNS_8* inbuf, UNS_32 dest_ipaddr, UNS_8 *pfile_name, TFtpLinkDir DataDir)
{
    UNS_32 msg_len = 0;

    // Put in Read Request or Write Request as appropriate
    *(UNS_16 *)Buffer = INTSWAP((DataDir == H2OLink) ? 1 : 2);
    msg_len = 2;

    strcpy((void *)(Buffer + 2), pfile_name);
#ifdef _DEBUG
    //    strcpy((void *)(Buffer + 2), "/_ix86.dbg/bin//elastos.img");
//    msg_len += 23;
    msg_len += strlen(pfile_name) + 1;
#else
    //    strcpy((void *)(Buffer + 2), "\\_arm.rls\\bin\\elastos.img");
    //msg_len += strlen(pfile_name) + 1;
//    msg_len += 26;
#endif

    // Specify octet (binary) mode
    strcpy((void *)(Buffer + msg_len), "octet");
    msg_len += 6;

    strcpy((void *)(Buffer + msg_len), "blksize");
    msg_len += 8;

    /* block_size = MAX_TFTP_DATA; //1432 */
    /*sprintf(Buffer, "%d", block_size);*/
    /*BUG_BUG_BUG   BUG_BUG_BUG     BUG_BUG_BUG*/
    /*sprintf() can't be routined, :-( */
    Buffer[msg_len] = 0x31;
    Buffer[msg_len + 1] = 0x34;
    Buffer[msg_len + 2] = 0x33;
    Buffer[msg_len + 3] = 0x32;
    Buffer[msg_len + 4] = 0x00;
    msg_len += 5;

    my_port = GenerateSrcPort();
    send_udp(inbuf, dest_ipaddr, TFTP_PORT, my_ipaddr, my_port, Buffer, msg_len);
}

//--------------------------------------------------------------------------
// This routine generates a pseudo random sequence using a 16-bit LFSR.
// These are used as source ports for the TFTP protocol.
// Note that because XOR was used, 0 is a dead value and will never be returned.
// The value of all 1's can be made the dead value if XNOR is used.
//--------------------------------------------------------------------------
//static
UNS_16 wLastValue = 1;
UNS_16 GenerateSrcPort()
{
    UNS_16 wTempValue;
    //int i;

    // Loop here until we get a value that isn't in use
    do {
        // For a 16-bit LFSR shifting to the left, I'll take bits 15,14,12,3
        wTempValue = 0xD008 & wLastValue;
        // Now XOR those bits together to get the bit shifted in on the right
        wTempValue ^= (wTempValue >> 12);
        wTempValue ^= (wTempValue >> 2);
        wTempValue ^= (wTempValue >> 1);

        // Shift it and OR in the new bit
        wLastValue = (wLastValue << 1) | (wTempValue & 0x0001);

        /*BUG: if some tftp service in use, a new wLastValue may be in use too,'
         then some wrong will be happen*/
        if (wLastValue < INTSWAP(1024)) {
            break;
        }
    } while (1);

    return wLastValue + 1024;
}

void RecvTftpData(UDP_DATA *pudp_data)
{
    /*have any udp data?*/
    if (pudp_data->length > 0) {
        /*data sent by tftp server?*/
        if (/*pudp_data->source_port == TFTP_PORT*/
            pudp_data->dest_port == my_port) {

            if (INTSWAP(*((UNS_16 *)pudp_data->pdata)) == 6) {
                *((UNS_16 *)Buffer) = INTSWAP(4);
                //*((UNS_16 *)Buffer + 1) = INTSWAP(nID);
                *((UNS_16 *)Buffer + 1) = 0;
                //nID++;
                memset(sendbuf, 0, sizeof(sendbuf));
                send_udp(sendbuf, remote_ipaddr, pudp_data->source_port,
                                my_ipaddr, my_port, Buffer, 4);
            }
            else {
                *((UNS_16 *)Buffer) = INTSWAP(4);
                *((UNS_16 *)Buffer + 1) = *((UNS_16 *)pudp_data->pdata + 1);

                if (INTSWAP(*((UNS_16 *)pudp_data->pdata)) == 3) {
                    UNS_32 block_id = 0;
                    block_id = INTSWAP(*((UNS_16 *)pudp_data->pdata + 1));
                    if (last_block_id != block_id) {
                        last_block_id = block_id;
                            /*recv data and save data*/
                        memcpy((save_addr + (block_id - 1) * MAX_TFTP_DATA),
                                        (UNS_8 *)((UNS_8 *)pudp_data->pdata + 4),
                                            (pudp_data->length - 4));
                            //if (!(block_id & 0xf ))
                        WriteByte('@');
                            /*is the last packet??*/
                        if ((pudp_data->length - 4) != MAX_TFTP_DATA) {
                                /*file length : nLength*/
                            nLength = (block_id - 1) * MAX_TFTP_DATA
                                            + pudp_data->length - 4;
                            BootupState = 3;
                            dputs("***** recv over *****\n" );
                        }
                    }
                }
                memset(sendbuf, 0, sizeof(sendbuf));
                /*send ack*/
                send_udp(sendbuf, remote_ipaddr, pudp_data->source_port,
                                my_ipaddr, my_port, Buffer, 4);
            }
        }
        /*clear data*/
        pudp_data->length = 0;
        pudp_data->pdata = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////
void InitInterrupt(void)
{
    gpio_port_f_bit_is_interrupt( 1 );      // cpld interrupt, disable all interrupt
    gpio_interrupt_debounce_enable( 1 );
    gpio_interrupt_level_triggered( 1 );
    gpio_interrupt_active_low( 1 );
    INTC->enableset |= INTC_GPIO1INTR;
    CPLD_SetIntMask( ALL_INT_MASK, 0 );
}

void FlashCopyToSDRam(void)
{
    UNS_32 *ptrScr, *ptrDst;
    UNS_32 i;

    ptrScr = (UNS_32 *)0x00000000;
    ptrDst = (UNS_32 *)0xc0000000;

    for (i = 0; i < 0x00020000 / 4; i++) {
        *ptrDst++ = *ptrScr++;
    }
}

void Timer2ISR(void)
{
    timer_int_clear( TIMER2 );
    NET_Loop25ms();
}

UNS_32 NetLoad(void)
{
    UNS_32 i, x, y;
    UNS_32 iret = 0;

    Lcd_PwOff();
    Lcd_LedOff();
    Dm_Close();

    InitInterrupt();

    Lcd_PwOn();
    Lcd_LedOn();
    Lcd_Init();
    Lcd_FillScreen(WHITE);

    timer_init( TIMER2 );                   // set timer2 periodic mode, 25ms
    timer_periodic( TIMER2 );
    timer_set_delay( TIMER2, 25000 );
    timer_int_enable( TIMER2 );
    timer_start( TIMER2 );

    if ( 0 == NET_config() ) goto exit;

    NET_Init();
    while (1) {
        if (Dm_waitForReset) {                                   // error, reset dm9000
            Dm_FastReset();
            Dm_waitForReset = FALSE;
        }

        NET_MainLoop();
        if (BootupState == 3) break;
    }

    timer_int_disable( TIMER2 );
    timer_stop( TIMER2 );
    Dm_Close();

exit:
    if (BootupState == 3) {
        if (bIsWriteFlash) nLength |= 0x80000000;
        iret = nLength;
    }

    CRT_Init();//LNOTENOTE: Must reset global valiable value
    return iret;
}

// This routine will take a binary IP address as represent
//here and return a dotted decimal version of it
static char* _ctoa(UNS_8 c, UNS_8*s)
{
    int a = (int)c;
    char v;
    for (v = '0'; a >= 100; v++) {
        a -= 100;
    }
    if (v != '0') *s++ = v;

    for (v = '0'; a >= 10; v++) {
        a -= 10;
    }
    *s++ = v;

    v = '0' + a;
    *s++ = v;

    return s;
}

void inet_ntoa(unsigned long uIP, UNS_8 *sz)
{
    //xxx.xxx.xxx.xxx

    sz = _ctoa((UNS_8 )uIP, sz);
    *sz++ = '.';

    sz = _ctoa((UNS_8)(uIP >> 8), sz);
    *sz++ = '.';

    sz = _ctoa((UNS_8)(uIP >> 16), sz);
    *sz++ = '.';

    sz = _ctoa((UNS_8)(uIP >> 24), sz);
    *sz = '\0';

    return;
}

// This routine will take a dotted decimal IP address
//as represent here and return a binary version of it
unsigned long inet_addr(UNS_8 *sz)
{
    unsigned long uIP = 0;
    unsigned long cBytes;
    UNS_8 *pszLastNum;

    // Replace the dots with NULL terminators
    pszLastNum = sz;
    for (cBytes = 0; cBytes < 4; cBytes++) {
        while (*sz != '.' && *sz != '\0')
            sz++;
        if (sz == '\0' && cBytes != 3)
            return 0;
        *sz = '\0';
        uIP |= (atoi(pszLastNum) & 0xFF) << (8*cBytes);
        pszLastNum = ++sz;
    }

    return uIP;
}

#define WAITTIME    (1000/25) * 10
int NET_config()
{
    UNS_8 szDotIP[17];
    int c = 0;

    dputs("\nPress [ENTER] to download through ETHNET or any key to abort ...");
    while (nTimerCount < WAITTIME) {//Wait one second
        if ( (c=ReadByteWithTimeout() ) > 0) break;
    }
    if (nTimerCount >= WAITTIME) {
        dputs("T\n");
        return 0;
    }
    else if (!('\r' == c || '\n' == c)) {
        dputs("C\n");
        return 0;
    }

    dputs("\nMy IP [");

    dputs(my_ip);
    dputs("]: ");

    dgets(my_ip);

    dputs("Remote IP [");
    dputs(remote_ip);
    dputs("]: ");

    dgets(remote_ip);

    dputs("Download image [");
    dputs(file_name);
    dputs("]: ");

    dgets(file_name);

    memcpy(szDotIP, my_ip, 17);
    my_ipaddr = inet_addr(szDotIP);

    memcpy(szDotIP, remote_ip, 17);
    remote_ipaddr = inet_addr(szDotIP);

    dputs("\nFROM [");
    inet_ntoa(remote_ipaddr, szDotIP);
    dputs(szDotIP);
    dputs("] TO [");
    inet_ntoa(my_ipaddr, szDotIP);
    dputs(szDotIP);
    dputs("]\n");

    dputs("Download [");
    dputs(file_name);

    dputs("]\n");
    dputs("Do you want to save image & ipconfig to flash?(Y/N): ");
    c = dgetc();
    if ('Y' == c || 'y' == c) {
        bIsWriteFlash = 1;
        dputs("OK WRITETOFLASH\n");
    }
    dputs("\nWait a second ...\n");

    // Create hwaddr from ipaddr
    my_hwaddr[4] = ((UNS_8*)&my_ipaddr)[2];
    my_hwaddr[5] = ((UNS_8*)&my_ipaddr)[3];

    return 1;//OK
}

extern UNS_16 ip_ident;
extern UNS_8  retries;
void CRT_Init()
{

    BootupState = 0;
    bIsWriteFlash = 0;
    nTimerCount = 0;
    last_block_id = 0;
    nLength = 0;

//static
    wLastValue = 1;
    ip_ident = 0;
    retries = 0;

    my_hwaddr[4] = 0;
    my_hwaddr[5] = 0;
}
